package com.minidb.storage;
import com.minidb.utils.Constants;
import java.util.*;

/**
 * BufferPool
 * - 支持 LRU / FIFO 两种替换策略
 * - 统计 hits / requests
 * - 提供单页刷新 flushPage 与 flushAll
 */
public class BufferPool {
    private final int capacity;
    private final FileManager fileManager;
    private final Constants.ReplacementPolicy policy;
    // 添加同步锁
    private final Object lock = new Object();
    // 修复：LRU需要accessOrder=true，但evict逻辑需要调整
    private final LinkedHashMap<String, Page> cache;
    private final Queue<String> fifoQueue = new LinkedList<>();
    private int hits = 0, requests = 0;

    public BufferPool(int capacity, FileManager fm, Constants.ReplacementPolicy policy) {
        this.capacity = capacity;
        this.fileManager = fm;
        this.policy = policy;
        // 修复：确保LRU策略下使用访问顺序
        this.cache = new LinkedHashMap<String, Page>(16, 0.75f, policy == Constants.ReplacementPolicy.LRU);
    }

    private String key(String table, int id) { return table + ":" + id; }

    public Page getPage(String table, int id) {
        synchronized (lock) {
            requests++;
            String k = key(table, id);
            if (cache.containsKey(k)) {
                hits++;
                return cache.get(k);
            }
            // 缓存不命中，从磁盘读页并放入缓存
            try {
                Page page = fileManager.readPage(table, id);
                if (page != null) {
                    putPage(table, page);
                }
                return page;
            } catch (Exception e) {
                // 改进错误处理，返回null而不是抛出异常
                System.err.println("读页失败: " + e.getMessage());
                return null;
            }
        }
    }

    public void putPage(String table, Page page) {
        synchronized (lock) {
            String k = key(table, page.getId());
            boolean isNewKey = !cache.containsKey(k);
            
            // 修复：在添加新页面前检查容量并执行替换
            if (isNewKey && cache.size() >= capacity) {
                evict();
            }
            
            cache.put(k, page);
            
            // FIFO队列管理：只有新key才加入队列
            if (policy == Constants.ReplacementPolicy.FIFO && isNewKey) {
                fifoQueue.offer(k);
            }
        }
    }

    private void evict() {
        String evictKey = null;
        if (policy == Constants.ReplacementPolicy.LRU) {
            // 修复：对于accessOrder=true的LinkedHashMap，第一个元素是最久未访问的
            evictKey = cache.keySet().iterator().next();
        } else {
            // FIFO策略：从队列头部取出最早的
            evictKey = fifoQueue.poll();
        }
        
        if (evictKey != null) {
            Page page = cache.remove(evictKey);
            // 清理FIFO队列中的对应项
            if (policy == Constants.ReplacementPolicy.FIFO) {
                fifoQueue.remove(evictKey);
            }
            
            if (page != null && page.isDirty()) {
                try {
                    String[] parts = evictKey.split(":");
                    fileManager.writePage(parts[0], page);
                } catch (Exception e) {
                    System.err.println("写页失败: " + e.getMessage());
                }
            }
            System.out.println("Evicting page " + evictKey + " (" + policy + ")");
        }
    }

    /** NEW: 单页刷新接口（flush_page） */
    public void flushPage(String table, int id) {
        String k = key(table, id);
        Page p = cache.get(k);
        if (p != null && p.isDirty()) {
            fileManager.writePage(table, p);
            p.setDirty(false);
        }
    }

    public void flushAll() {
        for (var e : cache.entrySet()) {
            Page p = e.getValue();
            if (p.isDirty()) {
                String[] parts = e.getKey().split(":");
                fileManager.writePage(parts[0], p);
                p.setDirty(false);
            }
        }
    }

    public double getHitRate() { return requests == 0 ? 0.0 : (double) hits / requests; }

    /**
     * 获取当前缓存中的页面数量
     * @return 当前缓存大小
     */
    public int getCurrentSize() {
        synchronized (lock) {
            return cache.size();
        }
    }
    
    /**
     * 重置缓存统计信息
     * 将命中次数和请求次数重置为0
     */
    public void resetStatistics() {
        synchronized (lock) {
            hits = 0;
            requests = 0;
        }
    }
}
